/** * ServletFormFactory - This Factory creates and recycles servlet forms for an application. * * Copyright (c) 2002 * Marty Phelan, All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ package com.taursys.servlet; import java.util.Hashtable; import java.util.Vector; /** * This Factory creates and recycles servlet forms for an application. * This class was designed to be used by a ServletApp. It will typically * be setup in the init method of the ServletApp. Below is an example of * typical usage: * <pre> * public class MyMainServlet extends ServletApp { * * public void init(ServletConfig config) throws ServletException { * super.init(config); * getFactory().addPackage("/","com.taursys.examples.simpleweb"); * getFactory().setDefaultFormName("com.taursys.examples.simpleweb.ShowHidePage"); * getFactory().setDefaultClassLoader(getClass().getClassLoader()); * // Set default logging * Debug.setLoggerAdapter(new SimpleLogger(Debug.DEBUG)); * } * } * </pre> */ public class ServletFormFactory { private Hashtable packages = new Hashtable(); private Hashtable servletForms = new Hashtable(); private String defaultFormName; private String servletFormSuffix = ".sf"; private ClassLoader defaultClassLoader; /** * Creates a new ServletFormFactory with a defaultFormName of com.taursys.servlet.DefaultMessageForm. * Also sets the defaultClassLoader to this class's classLoader */ public ServletFormFactory() { defaultFormName = "com.taursys.servlet.DefaultMessageForm"; defaultClassLoader = getClass().getClassLoader(); } /** * Returns a ServletForm based on the given url. This method will first see * if there are any recycled forms in the servletForms pool. If so, it will * remove that form from the pool and return it. If none are in the pool, * it will create a new one. This method depends on the parseClassName * method to determine the fully qualified class name of the ServletForm * based on the given url. * @param url containing the encoded ServletForm name * @return a ServletForm of the specific class requested in the url * @throws ServletFormNotFoundException if cannot parse url, package is not * registered, or ServletForm class is not found. */ public synchronized ServletForm createServletForm(String url) throws ServletFormNotFoundException { String servletFormName = parseClassName(url); Vector instances = (Vector)servletForms.get(servletFormName); // First try to get from pool ServletForm newForm = null; if (instances != null && instances.size() > 0) { newForm = (ServletForm)instances.remove(0); } // If none in pool then create a new one if (newForm == null ) { try { newForm = (ServletForm)defaultClassLoader.loadClass(servletFormName).newInstance(); } catch (Exception ex) { throw new ServletFormNotFoundException("Cannot create requested form: " + ex); } } return newForm; } /** * Recycle will put given servletForm back into pool if servletForm supports recycling. * The servletForm's recycle method returns true if it supports recycling. * @param usedServletForm to recycle (if it supports recycling) */ public synchronized void recycle(ServletForm usedServletForm) { if (usedServletForm.recycle()) { Vector instances = (Vector)servletForms.get( usedServletForm.getClass().getName()); if (instances == null) { instances = new Vector(); servletForms.put(usedServletForm.getClass().getName(), instances); } instances.add(usedServletForm); } } /** * Parses given url and constructs fully qualified ServletForm class name. * Returns the defaultFormName if the given url is null or "/". The * url is broken into 2 parts: path and form name. The package name is * retrieved from the packages table using the path as the key. The * class name is returned as the package name plus the form name (without * the servletFormSuffix). Examples: * <p> * Assuming 2 entries in package table: * <ul> * <li>path="/" packageName="com.taursys.example"</li> * <li>path="/admin" packageName="com.taursys.inv.admin"</li> * </ul> * Example translations: * <ul> * <li>url="/MyForm.sf" className="com.taursys.example.MyForm"</li> * <li>url="/admin/EditItem.sf" className="com.taursys.inv.admin.EditItem"</li> * </ul> * @param url path to parse - usually obtained through request.getPathInfo() * @return fully qualified class name string * @throws ServletFormNotFoundException if path not found or form does not * end in servletFormSuffix. */ public String parseClassName(String url) throws ServletFormNotFoundException { if (url != null && !url.equals("/")) { // Extract path/packageName first int lastSlash = url.lastIndexOf("/"); String urlPrefix = url.substring(0,lastSlash +1 ); String packageName = (String)packages.get(urlPrefix); if (packageName == null) throw new ServletFormNotFoundException( "Prefix " + urlPrefix + " not registered"); // Prepare and check form name String formName = url.substring(lastSlash + 1); int sLen = servletFormSuffix.length(); if (formName.length() < sLen + 1 || !formName.endsWith(servletFormSuffix)) throw new ServletFormNotFoundException("Name of ServletForm does not end in " + servletFormSuffix + ". url=" + url); // done return packageName + "." + formName.substring(0, formName.length() - sLen); } else { return defaultFormName; } } /** * Adds the given path and packageName to the packages table. The packages * table is used in the parseClassName method to determine the fully qualified * class name for a given path/ServletFormName. * @param path that form names will be registered under * @param packageName full package name for forms in given path */ public void addPackage(String path, String packageName) { if (path.endsWith("/")) packages.put(path, packageName); else packages.put(path + "/", packageName); } /** * Sets the default ServletForm name for this factory. This must be * a fully qualified class name. * @param newDefaultFormName for this factory */ public void setDefaultFormName(String newDefaultFormName) { defaultFormName = newDefaultFormName; } /** * Gets the default ServletForm name for this factory. * @return the defaultFormName for this factory */ public String getDefaultFormName() { return defaultFormName; } /** * Sets the suffix for ServletForms. The default is ".sf". * The suffix cannot be null, but it can be blank if no suffix is wanted. * @param newServletFormSuffix the suffix to use * @throws IllegalArgumentException if passed a null value. */ public void setServletFormSuffix(String newServletFormSuffix) { if (newServletFormSuffix == null) throw new java.lang.IllegalArgumentException( "servletFormSuffix cannot be null - use blank to indicate no suffix"); servletFormSuffix = newServletFormSuffix; } /** * Gets the current suffix for ServletForms. The default is ".sf". * @return the current suffix for ServletForms. */ public String getServletFormSuffix() { return servletFormSuffix; } /** * Set the default ClassLoader used to load and instantiate ServletForms. * This loader is initially set to the ClassLoader for this class. * Your ServletApp may need to set this property if the MapperXML classes * are loaded from a different ClassLoader than your application classes. * If you deploy your web application with mapperxml.jar in the * /WEB-INF/lib directory, then you will probably not need to set this * property. You will most commonly set this property within your * ServletApp's init method (example): * <pre> * getFactory().setDefaultClassLoader(getClass().getClassLoader()); * </pre> * @param newDefaultClassLoader to use to load and instantiate ServletForms. */ public void setDefaultClassLoader(ClassLoader newDefaultClassLoader) { defaultClassLoader = newDefaultClassLoader; } /** * Get the default ClassLoader used to load and instantiate ServletForms. * This loader is initially set to the ClassLoader for this class. * @return the default ClassLoader to use to load and instantiate ServletForms. */ public ClassLoader getDefaultClassLoader() { return defaultClassLoader; } }